Current File : /var/www/e360ban/wp-content/plugins/types/vendor/toolset/types/embedded/views/image.php |
<?php
// $cropped = types_image_crop( $img,
// array(
// 'image_crop' => 'crop',
// 'return' => 'object',
// 'width' => 150,
// 'height' => 150
// )
// );
/**
* API call.
*
*/
function types_image_resize( $img, $args = array() ) {
WPCF_Loader::loadView( 'image' );
$view = Types_Image_View::getInstance();
$args = wp_parse_args( $args,
array('return' => 'url', 'suppress_errors' => !TYPES_DEBUG) );
$resized = $view->resize( $img, $args );
if ( !is_wp_error( $resized ) ) {
switch ( $args['return'] ) {
case 'object':
return $resized;
break;
case 'path':
return $resized->path;
break;
case 'url':
default:
return $resized->url;
break;
}
} else if ( !$args['suppress_errors'] ) {
return $resized;
}
return $img;
}
/**
* API call.
*
* @param type $img
* @param type $args
* @return type
*/
function types_image_crop( $img, $args = array() ) {
return types_image_resize( $img, $args );
}
/**
* Types Image View class.
*
* @todo introduce __construct() to return static
*/
class Types_Image_View
{
private static $__singleton;
private static $__cache;
private static $__utils;
/**
* Construct.
*
* @return type
*/
private function __construct() {
self::$__cache = new Types_Cache();
self::$__utils = Types_Image_Utils::getInstance();
}
/**
* Returns instance.
*
* @return type
*/
public static function getInstance() {
if ( is_null( self::$__singleton ) ) {
self::$__singleton = new Types_Image_View();
}
return self::$__singleton;
}
/**
* Resize public method.
*
* @param type $img
* @param type $args
* @return type
*/
public function resize( $img, $args = array() ) {
if ( $cached = self::$__cache->getCache( func_get_args() ) ) {
return $cached;
}
return self::$__cache->setCache( func_get_args(),
self::resizeImg( $img, $args ) );
}
/**
* Crop public method.
*
* @param type $img
* @param type $args
* @return type
*/
public function crop( $img, $args = array() ) {
return self::resize( $img, $args );
}
/**
* Private resize method.
*
* @param type $img
* @param type $destination
* @param type $args
* @return type
*/
private static function resizeImg( $img, $args = array() ) {
if ( is_wp_error( $check = self::$__utils->checkEditRequirements( $img ) ) ) {
return $check;
}
if ( is_wp_error( $imgData = self::$__utils->getImg( $img ) ) ) {
return $imgData;
}
if ( is_wp_error( $path = self::$__utils->getWritablePath( $img ) ) ) {
return $path;
}
$args = wp_parse_args( $args,
array(
'resize' => 'proportional',
'padding_color' => '#FFF',
'clear_cache' => false,
'width' => 250,
'height' => 250,
)
);
if ( intval( $args['width'] ) < 1 ) {
$args['width'] = null;
}
if ( intval( $args['height'] ) < 1 ) {
$args['height'] = null;
}
if ( $args['width'] === null && $args['height'] === null ) {
return new WP_Error( __CLASS__ . '::' . __METHOD__,
"Could not calculate resized image dimensions {$img}", $args );
}
$dims = image_resize_dimensions( $imgData->width, $imgData->height,
intval( $args['width'] ), intval( $args['height'] ),
$args['resize'] == 'crop' );
if ( !$dims ) {
return new WP_Error( __CLASS__ . '::' . __METHOD__,
"Could not calculate resized image dimensions {$img}", $dims );
}
//
$__dst_w = intval( $args['width'] ) < 1 ? $dims[4] : intval( $args['width'] );
$__dst_h = intval( $args['height'] ) < 1 ? $dims[5] : intval( $args['height'] );
$basename = self::$__utils->basename( $img,
".{$imgData->pathinfo['extension']}" );
switch ( $args['resize'] ) {
case 'stretch':
$suffix = "{$__dst_w}x{$__dst_h}-stretched";
break;
case 'pad':
$_padding_color_suffix = $args['padding_color'] == 'transparent' ? 'transparent' : hexdec( $args['padding_color'] );
$suffix = "{$__dst_w}x{$__dst_h}-pad-" . $_padding_color_suffix;
break;
default:
$suffix = "{$dims[4]}x{$dims[5]}";
break;
}
$croppedImg = "{$path}{$basename}-wpcf_{$suffix}.{$imgData->pathinfo['extension']}";
if ( !$args['clear_cache'] ) {
if ( !is_wp_error( $cropped = self::$__utils->getImg( $croppedImg ) ) ) {
return $cropped;
}
}
/*
*
* Cropping
*/
$imgRes = self::$__utils->loadImg( $img );
if ( !is_resource( $imgRes ) ) {
return new WP_Error( __CLASS__ . '::' . __METHOD__,
"error_loading_image {$img}", $imgRes );
}
$dst_x = $dst_y = $src_x = $src_y = 0;
$dst_w = intval( $args['width'] );
$dst_h = intval( $args['height'] );
$src_w = $imgData->width;
$src_h = $imgData->height;
switch ( $args['resize'] ) {
case 'stretch':
$dst_w = $__dst_w;
$dst_h = $__dst_h;
$new_image = wp_imagecreatetruecolor( $__dst_w, $__dst_h );
break;
case 'crop':
default:
list($dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) = $dims;
$new_image = wp_imagecreatetruecolor( $dst_w, $dst_h );
break;
case 'pad':
$image_x = $imgData->width;
$image_y = $imgData->height;
$image_ar = $image_x / $image_y;
$disp_x = $dst_w = $__dst_w;
$disp_y = $dst_h = $__dst_h;
$disp_ar = $disp_x / $disp_y;
if ( $image_ar > $disp_ar ) {
$ratio = $disp_x / $image_x;
$dst_y = ($disp_y - $image_y * $ratio) / 2; // $offset_top
$dst_h = $disp_y - $dst_y * 2;
} else {
$ratio = $disp_y / $image_y;
$dst_x = ($disp_x - $image_x * $ratio) / 2; // $offset_left
$dst_w = $disp_x - $dst_x * 2;
}
$new_image = wp_imagecreatetruecolor( $__dst_w, $__dst_h );
if ( $args['padding_color'] == 'transparent' ) {
$t = imagecolorallocatealpha( $new_image, 255, 255, 255, 127 );
imagefill( $new_image, 0, 0, $t );
imagealphablending( $new_image, true );
} else {
$rgb = self::$__utils->hex2rgb( $args['padding_color'] );
$padding_color = imagecolorallocate( $new_image,
$rgb['red'], $rgb['green'], $rgb['blue'] );
imagefill( $new_image, 0, 0, $padding_color );
}
break;
}
$success = imagecopyresampled( $new_image, $imgRes,
$dst_x, // dst_x x-coordinate of destination point.
$dst_y, //dst_y y-coordinate of destination point.
$src_x, //src_x x-coordinate of source point. ,
$src_y, // src_y y-coordinate of source point.
$dst_w, // dst_w Destination width.
$dst_h, // dst_h Destination height.
$src_w, // src_w Source width.
$src_h// src_h Source height.
);
if ( !$success ) {
imagedestroy( $imgRes );
imagedestroy( $new_image );
return new WP_Error( __CLASS__ . '::' . __METHOD__,
"Error resampling image {$img}", $dims );
}
/* COMMENTED OUT TO NOT USE INDEXED COLORS FOR RESIZED PNGs, EVEN IF THE SOURCE HAS INDEXED COLORS.
* REASON: TRANSPARENT SUPPORT NOT WORKING 100% AFTER RESIZING USING INDEXED COLORS
*
// convert from full colors to index colors, like original PNG.
if ( IMAGETYPE_PNG == $imgData->imagetype && function_exists( 'imageistruecolor' ) && !imageistruecolor( $imgRes ) ) {
imagetruecolortopalette( $new_image, false,
imagecolorstotal( $imgRes ) );
}
*/
// we don't need the original in memory anymore
imagedestroy( $imgRes );
try {
if ( IMAGETYPE_GIF == $imgData->imagetype ) {
if ( !imagegif( $new_image, $croppedImg ) ) {
throw new Exception();
}
} else if ( IMAGETYPE_PNG == $imgData->imagetype ) {
if ( !imagepng( $new_image, $croppedImg ) ) {
throw new Exception();
}
} else {
// all other formats are converted to jpg
if ( 'jpg' != strtolower( $imgData->pathinfo['extension'] ) && 'jpeg' != strtolower( $imgData->pathinfo['extension'] ) ) {
$croppedImg = basename( $croppedImg,
$imgData->pathinfo['extension'] ) . '.jpg';
}
if ( !imagejpeg( $new_image, $croppedImg,
apply_filters( 'jpeg_quality', 90,
'image_resize' ) ) ) {
throw new Exception();
}
}
} catch ( Exception $e ) {
imagedestroy( $new_image );
return new WP_Error( __CLASS__ . '::' . __METHOD__,
__( 'Resize path invalid', 'wpcf' ), $croppedImg );
}
imagedestroy( $new_image );
// Set correct file permissions
$stat = stat( dirname( $croppedImg ) );
//same permissions as parent folder, strip off the executable bits
$perms = $stat['mode'] & 0000666;
@chmod( $croppedImg, $perms );
return self::$__utils->getImgObject( $croppedImg, $dst_w, $dst_h,
$imgData->imagetype, $imgData->mime );
}
}
/**
* Class Image Utilities.
*/
class Types_Image_Utils
{
const DESTINATION_DIR = '1970/01';
private static $__singleton;
private static $__cache;
public static $errors;
/**
* Construct.
*
* @return type
*/
private function __construct() {
WPCF_Loader::loadClass( 'path' );
WPCF_Loader::loadModel( 'image' );
self::$__cache = new Types_Cache();
self::$errors = new Types_Error();
}
/**
* Returns instance.
*
* @return Types_Image_Utils
*/
public static function getInstance() {
if ( is_null( self::$__singleton ) ) {
self::$__singleton = new Types_Image_Utils();
}
return self::$__singleton;
}
/**
* Checks if all requirements for editing image are set.
*
* @return \WP_Error|boolean
*/
public static function checkEditRequirements( $img ) {
if ( $cached = self::$__cache->getCache( 'check_edit_requirements' ) ) {
return $cached;
}
self::$errors->clearErrors();
// Check GD library
if ( !extension_loaded( 'gd' ) || !function_exists( 'gd_info' ) ) {
self::$errors->addError( 'GD library not present' );
}
// Check writable path
if ( is_wp_error( $path = self::getWritablePath( $img ) ) ) {
self::$errors->addError( $path );
}
// Check if any errors
if ( self::$errors->hasErrors() ) {
return self::$__cache->setCache( 'check_edit_requirements',
self::$errors );
}
return self::$__cache->setCache( 'check_edit_requirements', true );
}
/**
* Determines current writable path.
*
* @param $img
* @param bool $create_if_missing
*
* @return mixed
*/
public static function getWritablePath( $img = null, $create_if_missing = true ) {
if ( ! is_null( $img ) ) {
if ( $cached = self::$__cache->getCache( "writable_path_$img" ) ) {
return $cached;
}
$dir = dirname( $img ) . DIRECTORY_SEPARATOR;
if ( ! is_writable( $dir ) || ! is_dir( $dir ) ) {
return self::$__cache->setCache(
"writable_path_$img",
new WP_Error( __CLASS__ . '::' . __METHOD__, 'Destination dir not writable' )
);
}
return self::$__cache->setCache( "writable_path_$img", $dir );
}
if ( $cached = self::$__cache->getCache( 'temp_writable_path' ) ) {
return $cached;
}
$upload_info = self::uploadInfo();
if ( ! $upload_info ) {
return self::$__cache->setCache(
'temp_writable_path',
new WP_Error( __CLASS__ . '::' . __METHOD__, 'WP upload dir error' )
);
}
$dir = $upload_info['basedir'] . DIRECTORY_SEPARATOR . self::DESTINATION_DIR . DIRECTORY_SEPARATOR;
if ( ! $create_if_missing && ! file_exists( $dir ) ) {
// Do not create the directory if missing.
return null;
}
if (
! is_writable( $dir )
|| ! is_dir( $dir )
|| ! wp_mkdir_p( $dir )
) {
return self::$__cache->setCache(
'temp_writable_path',
new WP_Error( __CLASS__ . '::' . __METHOD__, 'Can not create writable dir' )
);
}
return self::$__cache->setCache( 'temp_writable_path', $dir );
}
/**
* Checks if image is valid.
*
* @param type $img
* @return type
* @throws Exception
*/
public static function getImg( $img ) {
if ( $cached = self::$__cache->getCache( $img ) ) {
return $cached;
}
self::$errors->clearErrors();
try {
if ( !is_file( $img ) || !is_readable( $img ) ) {
self::$errors->addError( 'File not readable', $img );
} else {
$size = @getimagesize( $img );
if ( !$size ) {
self::$errors->addError( 'Cannot read image size', $img );
}
}
if ( self::$errors->hasErrors() ) {
throw new Exception( 'Error found' );
}
} catch ( Exception $e ) {
return self::$__cache->setCache( $img, self::$errors );
}
list($imgWidth, $imgHeight, $imgType) = $size;
return self::$__cache->setCache( $img,
self::getImgObject( $img, $imgWidth, $imgHeight,
$imgType, $size['mime'] ) );
}
public static function getImgObject( $img, $width, $height, $type, $mime ) {
if ( $cached = self::$__cache->getCache( "imgobj_$img" ) ) {
return $cached;
}
if ( self::inUploadAbsPath( $img ) ) {
$url = self::normalizeAttachmentUrl( $img );
} else {
WPCF_Loader::loadClass( 'path' );
$url = WPCF_Path::getFileUrl( $img, false, false ) . '/' . basename( $img );
}
$data = array(
'width' => $width,
'height' => $height,
'imagetype' => $type,
'mime' => $mime,
'url' => $url,
'path' => $img,
'pathinfo' => pathinfo( $img ),
);
$imgData = new Types_Image_Model( (object) $data );
return self::$__cache->setCache( "imgobj_$img", $imgData->getImg() );
}
/**
* Loads image resource.
*
* @param type $img
* @return null
*/
public static function loadImg( $img ) {
if ( !is_file( $img ) || !function_exists( 'imagecreatefromstring' ) ) {
return null;
}
// Set artificially high because GD uses uncompressed images in memory
@ini_set( 'memory_limit',
apply_filters( 'image_memory_limit', WP_MAX_MEMORY_LIMIT ) );
$image = imagecreatefromstring( file_get_contents( $img ) );
return $image;
}
/**
* i18n friendly version of basename(), copy from wp-includes/formatting.php
* to solve bug with windows
*
* @param type $path
* @param type $suffix
* @return type
*/
public static function basename( $path, $suffix = '' ) {
return urldecode( basename( str_replace( array('%2F', '%5C'), '/',
urlencode( $path ) ), $suffix ) );
}
/**
* Convert hex to RGB.
*
* @param type $hex
* @return type
*/
public static function hex2rgb( $hex ) {
$hex = str_replace( "#", "", $hex );
if ( strlen( $hex ) == 3 ) {
$r = hexdec( substr( $hex, 0, 1 ) . substr( $hex, 0, 1 ) );
$g = hexdec( substr( $hex, 1, 1 ) . substr( $hex, 1, 1 ) );
$b = hexdec( substr( $hex, 2, 1 ) . substr( $hex, 2, 1 ) );
} else {
$r = hexdec( substr( $hex, 0, 2 ) );
$g = hexdec( substr( $hex, 2, 2 ) );
$b = hexdec( substr( $hex, 4, 2 ) );
}
return array('red' => $r, 'green' => $g, 'blue' => $b);
}
/**
* WP upload dir.
*
* @staticvar null $upload_info
* @return array|false
*/
public static function uploadInfo() {
static $upload_info = null;
if ( $upload_info === null ) {
$upload_info = @wp_upload_dir();
if( ! empty( $upload_info['error'] ) )
return false;
$parse_url = @parse_url( $upload_info['baseurl'] );
if ( $parse_url ) {
$baseurlpath = (!empty( $parse_url['path'] ) ? trim( $parse_url['path'],
'/' ) : '');
} else {
$baseurlpath = 'wp-content/uploads';
}
$upload_info['baseurlpath'] = '/' . $baseurlpath . '/';
$upload_info['path'] = self::realpath( $upload_info['path'] );
$upload_info['basedir'] = self::realpath( $upload_info['basedir'] );
$upload_info['subdir'] = self::realpath( $upload_info['subdir'] );
}
return $upload_info;
}
/**
* Returns realpath if applicable.
*
* @param string $path Absolute path to file
*/
public static function realpath( $path ) {
$realpath = @realpath( $path );
return $realpath ? $realpath : $path;
}
/**
* Normalize attachment URL.
*
* @param type $file
* @return string
*/
public static function normalizeAttachmentUrl( $file )
{
$upload_info = self::uploadInfo();
if ( $upload_info ) {
$file = ltrim( str_replace( $upload_info['basedir'], '', self::realpath( $file ) ), '/\\' );
$file = $upload_info['baseurl'] . '/' . str_replace( '\\', '/', $file );
}
return $file;
}
/**
* Returns path to file relative to upload_dir.
*
* @param $file Absolute path to file
* @return string '2014/01/img.jpg'
*/
public static function normalizeAttachment( $file ) {
$upload_info = self::uploadInfo();
if ( $upload_info ) {
$file = ltrim( str_replace( $upload_info['basedir'], '',
self::realpath( $file ) ), '/\\' );
$matches = null;
if ( preg_match( '~(\d{4}/\d{2}/)?[^/]+$~', $file, $matches ) ) {
$file = $matches[0];
}
}
return $file;
}
/**
* Get absolute path from URL.
*
* @param string $imgUrl
*
* @return string
*/
public static function getAbsPath( $imgUrl ) {
$upload_dir = wp_upload_dir();
$img_url_parts = parse_url( $imgUrl );
if ( $upload_dir
&& array_key_exists( 'baseurl', $upload_dir )
&& array_key_exists( 'basedir', $upload_dir )
&& ! empty( $upload_dir['baseurl'] )
&& ! empty( $upload_dir['basedir'] )
) {
$upload_dir_url_parts = parse_url( $upload_dir['baseurl'] );
// The baseurl may be some custom URL which doesn't necessarily contain a path.
// E.g. "http://images.mydomain.tld"
if ( ! array_key_exists( 'path', $upload_dir_url_parts ) ) {
$upload_dir_url_parts['path'] = '';
}
// Singlesite or Main site of Multisite
if (
// Pass if the path part of the base upload URL is empty - in that case $img_url_parts['path'] only
// contains the subpath.
( '' === $upload_dir_url_parts['path'] || 0 === strpos( $img_url_parts['path'], $upload_dir_url_parts['path'] ) )
&& ( ! is_multisite() || is_main_site() )
) {
// get dirname of relative image path -> '/wp-content/uploads/2018/07'
$image_relative_dir = dirname( $img_url_parts['path'] );
// substract the base dir of the $image_relative_dir -> '/2018/07'
$image_relative_dir_without_base = substr( $image_relative_dir, strlen( $upload_dir_url_parts['path'] ) );
// create the absolute path -> '/var/www/example.com/wp-content/uploads/2018/07/image-100x100.png'
$image_absolute_path = $upload_dir['basedir']
. $image_relative_dir_without_base
. DIRECTORY_SEPARATOR
. basename( $imgUrl );
// Check Multisite
} else if ( is_multisite() && !is_main_site() ) {
$multisite_parsed = explode( '/files/', $img_url_parts['path'] );
if ( isset( $multisite_parsed[1] ) ) {
$image_absolute_path = $upload_dir['basedir'] . DIRECTORY_SEPARATOR . dirname( $multisite_parsed[1] ) . DIRECTORY_SEPARATOR . basename( $imgUrl );
}
}
}
// Manual upload
$parsed_wp = parse_url( get_site_url() );
if ( empty( $image_absolute_path ) ) {
if ( !empty( $parsed_wp['path'] ) ) {
$image_absolute_path = dirname( str_replace( $parsed_wp['path'] . '/',
ABSPATH, $img_url_parts['path'] ) );
} else {
$image_absolute_path = ABSPATH . dirname( $img_url_parts['path'] );
}
$image_absolute_path .= DIRECTORY_SEPARATOR . basename( $imgUrl );
}
return $image_absolute_path;
}
/**
* Checks if file is in upload path.
*
* @param type $imgUrl
* @return boolean
*/
public static function inUploadPath( $imgUrl ) {
$upload_dir = self::uploadInfo();
if ( $upload_dir ) {
$parsed = parse_url( $imgUrl );
if ( !isset($upload_dir['baseurl'] ) ) {
return false;
}
$upload_dir_parsed = parse_url( $upload_dir['baseurl'] );
// This works for regular installation and main blog on multisite
if ( (!is_multisite() || is_main_site()) && strpos( $parsed['path'], $upload_dir_parsed['path'] ) === 0 ) {
return true;
// Check Multisite
} else if ( is_multisite() && !is_main_site() ) {
if ( strpos( $parsed['path'], $upload_dir_parsed['path'] ) === 0 ) {
return true;
}
$multisite_parsed = explode( '/files/', $parsed['path'] );
if ( isset( $multisite_parsed[1] ) ) {
return true;
}
}
}
return false;
}
public static function inUploadAbsPath( $img ) {
$upload_dir = self::uploadInfo();
if ( $upload_dir ) {
return strpos( $img, $upload_dir['basedir'] ) === 0;
}
return false;
}
/**
* Checks if file is on same domain.
*
* @param type $imgUrl
* @return type
*/
public static function onDomain( $imgUrl )
{
$parsed = parse_url( $imgUrl );
/**
* if 'host' do not exists as key in array $parsed,
* then it is relative url and is always on same
* domain, then return true
*/
if ( !array_key_exists('host', $parsed) ) {
return true;
}
/**
* compare site url to $imgUrl to check is on domain
*/
$parsed_wp = parse_url( get_site_url() );
if ( preg_match( '/(?P<domain>[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$/i', $parsed['host'], $regs ) ) {
$parsed['domain'] = $regs['domain'];
} else {
$parsed['domain'] = $parsed['host'];
}
if ( preg_match( '/(?P<domain>[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$/i', $parsed_wp['host'], $regs ) ) {
$parsed_wp['domain'] = $regs['domain'];
} else {
$parsed_wp['domain'] = $parsed_wp['host'];
}
return $parsed['domain'] == $parsed_wp['domain'];
}
}
/**
* Image model class.
*/
class Types_Image_Model
{
private $__img;
/**
* Init.
*
* @return type
*/
public function __construct( $img ) {
$this->__img = $img;
}
/**
* Returns image object.
*
* @return type
*/
public function getImg() {
return $this->__img;
}
}
/**
* Types Cache class.
*/
class Types_Cache
{
private $__cache = array();
/**
* Returns adjusted cache key.
*
* @param $key
* @return
*/
private function getCacheKey( $key ) {
return md5( maybe_serialize( $key ) );
}
/**
* Returns cache if available, otherwise false.
*
* @param $key
*
* @return bool|mixed
*/
public function getCache( $key ) {
$cache_key = $this->getCacheKey( $key );
return isset( $this->__cache[$cache_key] ) ? $this->__cache[$cache_key] : false;
}
/**
* Sets cache.
*
* @param $key
* @param $data
*
* @return mixed
*/
public function setCache( $key, $data ) {
$this->__cache[$this->getCacheKey( $key )] = $data;
return $data;
}
}
/**
* Types error class.
*
* @deprecated Do not use anywhere new.
* @since the year 2013
*/
class Types_Error extends WP_Error
{
/**
* Init function.
*
* @param $code
* @param $message
* @param $data
*/
function __construct( $code = 'types_error', $message = '', $data = '' ) {
parent::__construct( $code, $message, $data );
$this->clearErrors();
}
/**
* Adds error and debug data.
*
* @param $message
* @param $data
*/
public function addError( $message, $data = array() ) {
if ( $message instanceof WP_Error ) {
$code = $message->get_error_code();
$message = $message->get_error_message();
if( isset( $message->error_data ) ) {
$data = $message->error_data;
}
} else {
// phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.Changed
$db = debug_backtrace();
$code = "{$db[1]['class']}::{$db[1]['function']}()";
}
parent::add( $code, $message, $data );
}
/**
* Clear all errors (reset)
*/
public function clearErrors() {
$this->errors = array();
$this->error_data = array();
}
/**
* Returns errors (all codes).
*
* @return type
*/
public function getErrors() {
return $this->errors;
}
/**
* Checks if has errors.
*
* @return type
*/
public function hasErrors() {
return (bool) count( $this->errors );
}
}